package visiopuce.graph;

import java.util.Iterator;
import java.util.List;

import org.jfree.data.general.AbstractSeriesDataset;
import org.jfree.data.general.SeriesChangeEvent;
import org.jfree.util.ObjectUtilities;
import org.jfree.util.PublicCloneable;

/**
 * A collection of {@link FloatingBarSeries} objects. This class provides one
 * implementation of the {@link FloatingBarCategoryDataset} interface.
 */
public class FloatingBarSeriesCollection extends AbstractSeriesDataset
		implements FloatingBarCategoryDataset, PublicCloneable
		 {

	/** For serialization. */
	private static final long serialVersionUID = -2065799050738449903L;

	/**
	 * Storage for aggregate floatingBar keys (the floatingBar description is
	 * used as the key).
	 */
	private List keys;

	/** Storage for the series. */
	private List data;

	/**
	 * Default constructor.
	 */
	public FloatingBarSeriesCollection() {
		this.keys = new java.util.ArrayList();
		this.data = new java.util.ArrayList();
	}

	

	/**
	 * Returns a series from the collection.
	 * 
	 * @param series
	 *            the series index (zero-based).
	 * 
	 * @return The series.
	 * 
	 * @since 1.0.1
	 */
	public FloatingBarSeries getSeries(int series) {
		if ((series < 0) || (series >= getSeriesCount())) {
			throw new IllegalArgumentException("Series index out of bounds");
		}
		return (FloatingBarSeries) this.data.get(series);
	}

	/**
	 * Returns the number of series in the collection.
	 * 
	 * @return The series count.
	 */
	public int getSeriesCount() {
		return getRowCount();
	}

	/**
	 * Returns the name of a series.
	 * 
	 * @param series
	 *            the series index (zero-based).
	 * 
	 * @return The name of a series.
	 */
	public Comparable getSeriesKey(int series) {
		FloatingBarSeries ts = (FloatingBarSeries) this.data.get(series);
		return ts.getKey();
	}

	/**
	 * Returns the number of rows (series) in the collection.
	 * 
	 * @return The series count.
	 */
	public int getRowCount() {
		return this.data.size();
	}

	/**
	 * Returns the row keys. In this case, each series is a key.
	 * 
	 * @return The row keys.
	 */
	public List getRowKeys() {
		return this.data;
	}

	/**
	 * Returns the number of column in the dataset.
	 * 
	 * @return The column count.
	 */
	public int getColumnCount() {
		return this.keys.size();
	}

	/**
	 * Returns a list of the column keys in the dataset.
	 * 
	 * @return The category list.
	 */
	public List getColumnKeys() {
		return this.keys;
	}

	/**
	 * Returns a column key.
	 * 
	 * @param index
	 *            the column index.
	 * 
	 * @return The column key.
	 */
	public Comparable getColumnKey(int index) {
		return (Comparable) this.keys.get(index);
	}

	/**
	 * Returns the column index for a column key.
	 * 
	 * @param columnKey
	 *            the column key (<code>null</code> not permitted).
	 * 
	 * @return The column index.
	 */
	public int getColumnIndex(Comparable columnKey) {
		if (columnKey == null) {
			throw new IllegalArgumentException("Null 'columnKey' argument.");
		}
		return this.keys.indexOf(columnKey);
	}

	/**
	 * Returns the row index for the given row key.
	 * 
	 * @param rowKey
	 *            the row key.
	 * 
	 * @return The index.
	 */
	public int getRowIndex(Comparable rowKey) {
		int result = -1;
		int count = this.data.size();
		for (int i = 0; i < count; i++) {
			FloatingBarSeries s = (FloatingBarSeries) this.data.get(i);
			if (s.getKey().equals(rowKey)) {
				result = i;
				break;
			}
		}
		return result;
	}

	/**
	 * Returns the key for a row.
	 * 
	 * @param index
	 *            the row index (zero-based).
	 * 
	 * @return The key.
	 */
	public Comparable getRowKey(int index) {
		FloatingBarSeries series = (FloatingBarSeries) this.data.get(index);
		return series.getKey();
	}

	/**
	 * Adds a series to the dataset and sends a
	 * {@link org.jfree.data.general.DatasetChangeEvent} to all registered
	 * listeners.
	 * 
	 * @param series
	 *            the series (<code>null</code> not permitted).
	 */
	public void add(FloatingBarSeries series) {
		if (series == null) {
			throw new IllegalArgumentException("Null 'series' argument.");
		}
		this.data.add(series);
		series.addChangeListener(this);

		// look for any keys that we don't already know about...
		Iterator iterator = series.getFloatingBars().iterator();
		while (iterator.hasNext()) {
			FloatingBar floatingBar = (FloatingBar) iterator.next();
			String key = floatingBar.getDescription();
			int index = this.keys.indexOf(key);
			if (index < 0) {
				this.keys.add(key);
			}
		}
		fireDatasetChanged();
	}


	/**
	 * Returns the value for an item.
	 * 
	 * @param rowKey
	 *            the row key.
	 * @param columnKey
	 *            the column key.
	 * 
	 * @return The item value.
	 */
	public Number getValue(Comparable rowKey, Comparable columnKey) {
		return getStartValue(rowKey, columnKey);
	}

	/**
	 * Returns the value for a floatingBar.
	 * 
	 * @param row
	 *            the row index (zero-based).
	 * @param column
	 *            the column index (zero-based).
	 * 
	 * @return The start value.
	 */
	public Number getValue(int row, int column) {
		return getStartValue(row, column);
	}

	/**
	 * Returns the start value for a floatingBar.
	 * 
	 * @param rowKey
	 *            the series.
	 * @param columnKey
	 *            the category.
	 * 
	 * @return The start value (possibly <code>null</code>).
	 */
	public Number getStartValue(Comparable rowKey, Comparable columnKey) {
		Number result = null;
		int row = getRowIndex(rowKey);
		FloatingBarSeries series = (FloatingBarSeries) this.data.get(row);
		FloatingBar floatingBar = series.get(columnKey.toString());
		if (floatingBar != null) {
			result = floatingBar.getStart();
		}
		return result;
	}

	/**
	 * Returns the start value for a floatingBar.
	 * 
	 * @param row
	 *            the row index (zero-based).
	 * @param column
	 *            the column index (zero-based).
	 * 
	 * @return The start value.
	 */
	public Number getStartValue(int row, int column) {
		Comparable rowKey = getRowKey(row);
		Comparable columnKey = getColumnKey(column);
		return getStartValue(rowKey, columnKey);
	}

	/**
	 * Returns the end value for a floatingBar. This is a date/time value,
	 * measured in milliseconds since 1-Jan-1970.
	 * 
	 * @param rowKey
	 *            the series.
	 * @param columnKey
	 *            the category.
	 * 
	 * @return The end value (possibly <code>null</code>).
	 */
	public Number getEndValue(Comparable rowKey, Comparable columnKey) {
		Number result = null;
		int row = getRowIndex(rowKey);
		FloatingBarSeries series = (FloatingBarSeries) this.data.get(row);
		FloatingBar floatingBar = series.get(columnKey.toString());
		if (floatingBar != null) {
			result = floatingBar.getEnd();
		}
		return result;
	}

	/**
	 * Returns the end value for a task.
	 * 
	 * @param row
	 *            the row index (zero-based).
	 * @param column
	 *            the column index (zero-based).
	 * 
	 * @return The end value.
	 */
	public Number getEndValue(int row, int column) {
		Comparable rowKey = getRowKey(row);
		Comparable columnKey = getColumnKey(column);
		return getEndValue(rowKey, columnKey);
	}

	/**
	 * Returns the percent complete for a given item.
	 * 
	 * @param row
	 *            the row index (zero-based).
	 * @param column
	 *            the column index (zero-based).
	 * 
	 * @return The percent complete (possibly <code>null</code>).
	 */
	public Number getPercentComplete(int row, int column) {
		Comparable rowKey = getRowKey(row);
		Comparable columnKey = getColumnKey(column);
		return getPercentComplete(rowKey, columnKey);
	}

	/**
	 * Returns the percent complete for a given item.
	 * 
	 * @param rowKey
	 *            the row key.
	 * @param columnKey
	 *            the column key.
	 * 
	 * @return The percent complete.
	 */
	public Number getPercentComplete(Comparable rowKey, Comparable columnKey) {
		Number result = null;
		int row = getRowIndex(rowKey);
		FloatingBarSeries series = (FloatingBarSeries) this.data.get(row);
		FloatingBar floatingBar = series.get(columnKey.toString());
		if (floatingBar != null) {
			result = floatingBar.getPercentComplete();
		}
		return result;
	}




	/**
	 * Called when a series belonging to the dataset changes.
	 * 
	 * @param event
	 *            information about the change.
	 */
	public void seriesChanged(SeriesChangeEvent event) {
		refreshKeys();
		fireDatasetChanged();
	}

	/**
	 * Refreshes the keys.
	 */
	private void refreshKeys() {

		this.keys.clear();
		for (int i = 0; i < getSeriesCount(); i++) {
			FloatingBarSeries series = (FloatingBarSeries) this.data.get(i);
			// look for any keys that we don't already know about...
			Iterator iterator = series.getFloatingBars().iterator();
			while (iterator.hasNext()) {
				FloatingBar floatingBar = (FloatingBar) iterator.next();
				String key = floatingBar.getDescription();
				int index = this.keys.indexOf(key);
				if (index < 0) {
					this.keys.add(key);
				}
			}
		}

	}

	/**
	 * Tests this instance for equality with an arbitrary object.
	 * 
	 * @param obj
	 *            the object (<code>null</code> permitted).
	 * 
	 * @return A boolean.
	 */
	public boolean equals(Object obj) {
		if (obj == this) {
			return true;
		}
		if (!(obj instanceof FloatingBarSeriesCollection)) {
			return false;
		}
		FloatingBarSeriesCollection that = (FloatingBarSeriesCollection) obj;
		if (!ObjectUtilities.equal(this.data, that.data)) {
			return false;
		}
		return true;
	}

	/**
	 * Returns an independent copy of this dataset.
	 * 
	 * @return A clone of the dataset.
	 * 
	 * @throws CloneNotSupportedException
	 *             if there is some problem cloning the dataset.
	 */
	public Object clone() throws CloneNotSupportedException {
		FloatingBarSeriesCollection clone = (FloatingBarSeriesCollection) super
				.clone();
		clone.data = (List) ObjectUtilities.deepClone(this.data);
		clone.keys = new java.util.ArrayList(this.keys);
		return clone;
	}


}
